#include "Scope.h"

#include "InCommentC.h"
#include "InCommentCPP.h"
#include "InGlobal.h"
#include "InParen.h"
#include "InBlock.h"
#include "InBracket.h"
#include "InPreProcessor.h"
#include "InLiteral.h"

#include<string>
#include<iostream>
#include <boost/algorithm/string.hpp>

using namespace std;

Scope::Scope()
{
    m_eType=Scope::eScopeInvalid;
    m_pxParent = NULL;
	m_asElement.push_back("");
	m_sName = "";

	m_sLastKeyword = "";

	m_pxSymbolTable = new SymbolTable(this);
}

Scope::~Scope()
{
}

//Do not call for Comment scope types
Scope* Scope::parse(Token &inToken, bool &bIsEndOfScope)
{
    Scope* pxScope = NULL;
	bIsEndOfScope=bIsEndOfScope; //to suppress warning
    char cDelimiter = char(inToken);
    
    //Save the token
    m_axBody.push_back(inToken);

	if(inToken.m_bIsKeyword)
	{
		m_sLastKeyword = inToken.m_sValue;
	}
	
	if(pxScope == NULL)
    //Check for new scope opening character, if not comment
    {
		if(inToken.m_sValue == "/*")	pxScope = new InCommentC(this); else 
		if(inToken.m_sValue == "//")  	pxScope = new InCommentCPP(this); else
		switch(cDelimiter)
        {
        case '{':
            pxScope = new InBlock(this);
            break;

        case '(':
            pxScope = new InParen(this);
            break;

        case '[':
            pxScope = new InBracket(this);
            break;

        case '#':
            pxScope = new InPreProcessor(this);
            break;

        case '\'':
        case '"':
            pxScope = new InLiteral(this, cDelimiter);
            break;

        case '/':
        case ' ':
        case '\n':
        case '\r':
        case '\t':
            break;

        case ')':
        case '}':
        case ']':
			m_asElement.push_back("");
           break;

        case ',':
            break;

        case ';':
			m_asElement.push_back("");
			m_sLastKeyword = "";
            break;

        default:
			if(!inToken.m_bIsDelimiter)
				m_asElement.back() += " ";
			m_asElement.back() += inToken.m_sValue;
            break;
        }
    }

    if(pxScope != NULL)
    {
        m_apxChild.push_back(pxScope);
    }
    return pxScope;
}

string& Scope::getName()
{
	return this->m_sName;
}

string Scope::getText()
{
	int i;
	int n = m_axBody.size();
	string sOutput ="";

	for(i=0;i<n;i++)
	{
		if(m_axBody[i].m_sValue !="") //We don't want the NULL children markers
			sOutput+= m_axBody[i].m_sValue;
	}
	return sOutput;
}

bool Scope::isCode()
{
	switch(m_eType)
	{
	case eScopeLiteral:
	case eScopeCommentC:
	case eScopeCommentCPP:
	case eScopeInvalid:
		return false;
	}
	return true;
}

bool Scope::canDefineFunction()
{
	return false;
}

bool Scope::canDefineType()
{
	return false;
}

bool Scope::canDefineVariable()
{
	if(!isCode())	return false;

	switch(m_eType)
	{
	case eScopeBracket:
		return false;
	}
	
	return true;
}

bool Scope::isFunction()
{
	return false;
}

bool Scope::isComment()
{
	switch(m_eType)
	{
	case eScopeCommentC:
	case eScopeCommentCPP:
		return true;
	}
	return false;
}

bool Scope::findSymbol(string& in_sTestSymbol, Symbol& out_Symbol)
{
	out_Symbol.m_eSymbolType = Symbol::eSymbolInvalid;

	//Check in current scope's symbol-table
	if(m_pxSymbolTable->isPresent(in_sTestSymbol, out_Symbol))
		return true;
	
	//Check in parent-scope's SymbolTable
	if(m_pxParent!=NULL)
	{
		if(m_pxParent->findSymbol(in_sTestSymbol, out_Symbol))
			return true;
	}

	if(findSymbolInRelatives(this, in_sTestSymbol, out_Symbol))
		return true;
	//Check in related scopes, recursively (for multiple level of include)
	return false;
}

bool Scope::findSymbolInRelatives(Scope* in_pxScope, string& in_sTestSymbol, Symbol& out_Symbol)
{
	//Check in related scopes, recursively (for multiple level of include)
	int nRelatedScopes = in_pxScope->m_apxRelative.size();
	Scope* pxCursor = NULL;

	if(in_pxScope->m_pxSymbolTable->isPresent(in_sTestSymbol, out_Symbol))
		return true;

	for(int i=0; i<nRelatedScopes; i++)
	{
		pxCursor = in_pxScope->m_apxRelative[i];
		if(pxCursor == NULL) //Defensive you know, just in case
			continue;

		if(findSymbolInRelatives(pxCursor, in_sTestSymbol, out_Symbol))
			return true;
	}

	return false;
}

void Scope::addRelative(Scope* in_pxScope)
{
	m_apxRelative.push_back(in_pxScope);
}

void Scope::print()
{
    int nWorkspaceSize;
    int nWorkspaceLooper;
    int nChildLooper;
    string sTemp;

    nWorkspaceSize = (int)m_axBody.size();

    nChildLooper=0;
    for(nWorkspaceLooper = 0; nWorkspaceLooper<nWorkspaceSize; nWorkspaceLooper++)
    {
		sTemp = m_axBody[nWorkspaceLooper].m_sValue;
        if(sTemp == "")
        {
            m_apxChild[nChildLooper]->print();
            nChildLooper++;
        }
        cout<<sTemp;
    }
}

void Scope::printSymbols()
{
    int i;
    int nChildren;
    string sTemp;
	Scope* pxChildScope = NULL;

	int nSymbols = m_pxSymbolTable->getSize();
	if(nSymbols==0) return;

	cout << "Scope:" << m_sName << " has:" << nSymbols << " symbols" << endl;
	m_pxSymbolTable->print();

    nChildren = (int)m_apxChild.size();
    for(i = 0; i<nChildren; i++)
    {
		pxChildScope = m_apxChild[i];
		if(pxChildScope != NULL)
			pxChildScope->printSymbols();
    }

}

bool Scope::findScope(std::string in_sScopeName, Scope* &out_pxScope)
{
	Symbol xSymbol;
	out_pxScope = NULL;

	boost::replace_all(in_sScopeName, "*", "");
	if(m_sName == in_sScopeName){
		out_pxScope = this;
		return true;
	}
	
	//Check parent
	if(m_pxParent!=NULL)
	{
		if(m_pxParent->findScope(in_sScopeName, out_pxScope)){
			return true;
		}
	}

	//Check in related scopes
	int nRelatedScopes = m_apxRelative.size();
	Scope* pxCursor = NULL;

	for(int i=0; i<nRelatedScopes; i++)
	{
		pxCursor = m_apxRelative[i];
		if(pxCursor == NULL) //Defensive just in case
			continue;
		out_pxScope = findChildScopeWithName(pxCursor, in_sScopeName);
		if(out_pxScope!=NULL)
			return true;
	}

	out_pxScope = findChildScopeWithName(this, in_sScopeName);
	if(out_pxScope) return true;
	return false;
}

Scope* Scope::findChildScopeWithName(Scope* in_pxScope, std::string in_sScopeName)
{
	//Check in child scopes
	int nChildren = in_pxScope->m_apxChild.size();
	Scope* pxCursor = NULL;

	if(in_pxScope->m_sName == in_sScopeName){
		return in_pxScope;
	}

	for(int i=0; i<nChildren; i++)
	{
		pxCursor = in_pxScope->m_apxChild[i];
		if(pxCursor == NULL) //Defensive just in case
			continue;
	}
	return NULL;
}

